package com.xnfh.framework.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.Log;

import androidx.annotation.NonNull;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;

import java.util.Hashtable;
import java.util.Map;

/**
 * Created by wmmeng on 2020/10/23
 *
 * @descr
 */
public class QRCodeUtils {
    private static final String TAG = "QRCodeUtils";
    private static final int DEFAULT_BG_COLOR = 0xffffffff;
    private static final int DEFAULT_FG_COLOR = Color.BLACK;
    private static final int FRAME_WIDTH = 0;
    private static final int QUIET_ZONE_SIZE = 4;

    /**
     * 将文字编码为二维码图片，采用UTF-8编码，不包含logo
     *
     * @param text
     * @param width
     * @param height
     */
    private static Bitmap encode(String text, int width, int height, int padding) {
        return getQRCodeReal(text, width, height, DEFAULT_BG_COLOR, DEFAULT_FG_COLOR, padding,
                ErrorCorrectionLevel.L);
    }

    /**
     * 将文字编码为二维码图片，采用UTF-8编码，显示指定logo
     *
     * @param context
     * @param text
     * @param width
     * @param height
     * @param logoRes
     * @return
     */
    public static Bitmap encode(@NonNull Context context, String text, int width, int height, int logoRes) {
        return encode(context, text, width, height, logoRes, 0);
    }

    /**
     * 将文字编码为二维码图片，采用UTF-8编码，显示指定logo
     *
     * @param context
     * @param text
     * @param width
     * @param height
     * @param logoRes
     * @param padding
     * @return
     */
    public static Bitmap encode(@NonNull Context context, String text, int width, int height, int logoRes, int padding) {
        Bitmap logo0 = logoRes == 0 ? null : BitmapFactory.decodeResource(context.getResources(), logoRes);
        Bitmap qrcode = getQRCodeReal(text, width, height, DEFAULT_BG_COLOR, DEFAULT_FG_COLOR, padding,
                ErrorCorrectionLevel.L);
        return compose(qrcode, getLogo(width, height, logo0));
    }

    /**
     * 将文字编码为二维码图片，采用UTF-8编码，不包含logo
     *
     * @param context
     * @param text
     * @param width
     * @param height
     * @param bgColor
     * @param fgColor
     * @param logoRes
     * @return
     */
    public static Bitmap encode(@NonNull Context context, String text, int width, int height, int bgColor, int fgColor, int logoRes) {
        return encode(context, text, width, height, bgColor, fgColor, logoRes, 0);
    }

    /**
     * 将文字编码为二维码图片，采用UTF-8编码，不包含logo
     *
     * @param context
     * @param text
     * @param width
     * @param height
     * @param bgColor
     * @param fgColor
     * @param logoRes
     * @param padding padding 单位px
     * @return
     */
    public static Bitmap encode(@NonNull Context context, String text, int width, int height, int bgColor, int fgColor, int logoRes, int padding) {
        Bitmap logo0 = logoRes == 0 ? null : BitmapFactory.decodeResource(context.getResources(), logoRes);
        Bitmap qrcode = getQRCodeReal(text, width, height, bgColor, fgColor, padding,
                ErrorCorrectionLevel.H);
        return compose(qrcode, getLogo(width, height, logo0));
    }

    /**
     * 将文字编码为二维码图片，采用UTF-8编码，不包含logo
     *
     * @param context
     * @param text
     * @param width
     * @param height
     * @param bgColor
     * @param fgColor
     * @param logoRes
     * @param padding padding 单位px
     * @return
     */
    public static Bitmap encode(@NonNull Context context, String text, int width, int height, int bgColor, int fgColor, int logoRes, int padding, ErrorCorrectionLevel errorCorrectionLevel) {
        Bitmap logo0 = logoRes == 0 ? null : BitmapFactory.decodeResource(context.getResources(), logoRes);
        Bitmap qrcode = getQRCodeReal(text, width, height, bgColor, fgColor, padding,
                errorCorrectionLevel);
        return compose(qrcode, getLogo(width, height, logo0));
    }

    private static Bitmap compose(Bitmap bg, Bitmap fg) {
        if (fg != null) {
            Canvas canvas = new Canvas(bg);
            //fg
            Paint paint = new Paint();
            float fgX = (bg.getWidth() - fg.getWidth()) / 2f;
            float fgY = (bg.getHeight() - fg.getHeight()) / 2f;
            canvas.drawBitmap(fg, fgX, fgY, paint);
            canvas.save();
            fg.recycle();
        }
        return bg;
    }

    private static Bitmap getLogo(int width, int height, Bitmap logo0) {
        if (logo0 == null) {
            return null;
        }
        int targetWidth = (int) (width / 4f);
        int targetHeight = (int) (height / 4f);
        Bitmap logo = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(logo);

        Rect src = new Rect(0, 0, logo0.getWidth(), logo0.getHeight());
        Rect des = new Rect(0, 0, targetWidth, targetHeight);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        c.drawBitmap(logo0, src, des, paint);
        c.save();

        if (!logo0.isRecycled()) {
            logo0.recycle();
        }
        return logo;
    }

    /**
     * 生成二维码图片
     *
     * @param text
     * @param w
     * @param h
     * @param bgColor
     * @param fgColor
     * @param padding
     * @param errorCorrectionLevel
     * @return
     */
    private static Bitmap getQRCodeReal(String text, int w, int h, int bgColor, int fgColor,
                                        int padding, ErrorCorrectionLevel errorCorrectionLevel) {
        if (text == null || "".equals(text) || text.length() < 1) {
            return null;
        }
        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 0);
        hints.put(EncodeHintType.ERROR_CORRECTION, errorCorrectionLevel);

        // 图像数据转换，使用了矩阵转换
        try {

            BitMatrix bitMatrix = encode(text, BarcodeFormat.QR_CODE, w, h, hints);
            int[] pixels = new int[w * h];
            // 下面按照二维码的算法，逐个生成二维码的图片
            // 两个for循环是图片横列扫描的结果
            int startPoint = -1; // 内容较少时可能有padding，需要截掉
            Rect contentRect = new Rect();

            for (int y = 0; y < h; y++) {
                for (int x = 0; x < w; x++) {
                    if (bitMatrix.get(x, y)) {
                        if (contentRect.isEmpty()) {
                            contentRect.set(x, y, x + 1, y + 1);
                        } else {
                            if (x > contentRect.right) {
                                contentRect.right = x;
                            }
                            if (y > contentRect.bottom) {
                                contentRect.bottom = y;
                            }
                        }
                        pixels[y * w + x] = fgColor;
                    } else {
                        pixels[y * h + x] = bgColor;
                    }
                }
            }

            // 注意：canvas.drawBitmap时使用的src Rectangle，right和bottom是实际像素范围+1
            if (contentRect.right < w)
                contentRect.right++;
            if (contentRect.bottom < h)
                contentRect.bottom++;
            // 生成二维码图片的格式，使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, w, 0, 0, w, h);

            if (padding < 0 || padding * 2 > Math.min(w, h)) {
                Log.e(TAG, "Illegal padding:" + padding + ", modify padding to 0 px");
                padding = 0;
            }

            // 结果的padding和期望的padding不同
            if (contentRect.left != padding || contentRect.top != padding) {
                Bitmap oldBitmap = bitmap;
                bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);
                // canvas.drawColor(bgColor);
                if (padding > 0) {
                    Paint paint = new Paint();
                    paint.setColor(bgColor);
                    canvas.drawRect(padding, 0, w, padding, paint);
                    canvas.drawRect(w - padding, padding, w, h, paint);
                    canvas.drawRect(0, h - padding, w, h, paint);
                    canvas.drawRect(0, 0, padding, h - padding, paint);
                }
                canvas.drawBitmap(oldBitmap, contentRect, new Rect(padding, padding, w - padding, h - padding), null);
                oldBitmap.recycle();
            }

            // 显示到一个ImageView上面
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成二维码图片
     *
     * @param text
     * @param width
     * @param height
     * @param bgColor
     * @param fgColor
     * @return
     */
    private static Bitmap encode(String text, int width, int height, int bgColor, int fgColor, Bitmap logo) {
        if (text == null || "".equals(text) || text.length() < 1) {
            return null;
        }
        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 0);

        int[][] srcPixels = null;
        int IMAGE_HALF_WIDTH = 0;
        if (logo != null) {
            int IMAGE_WIDTH = logo.getWidth();
            int IMAGE_HEIGHT = logo.getHeight();

            IMAGE_HALF_WIDTH = IMAGE_WIDTH / 2;

            srcPixels = new int[IMAGE_WIDTH][IMAGE_HEIGHT];
            for (int i = 0; i < logo.getWidth(); i++) {
                for (int j = 0; j < logo.getHeight(); j++) {
                    srcPixels[i][j] = logo.getPixel(i, j);
                }
            }
            logo.recycle();
        }

        // 图像数据转换，使用了矩阵转换
        try {
            BitMatrix matrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);
            // 二维矩阵转为一维像素数组
            int halfW = width / 2;
            int halfH = height / 2;
            int[] pixels = new int[width * height];

            for (int y = 0; y < matrix.getHeight(); y++) {
                for (int x = 0; x < matrix.getWidth(); x++) {
                    // 读取图片
                    if (logo != null && x > halfW - IMAGE_HALF_WIDTH
                            && x < halfW + IMAGE_HALF_WIDTH
                            && y > halfH - IMAGE_HALF_WIDTH
                            && y < halfH + IMAGE_HALF_WIDTH) {
                        pixels[y * width + x] = 0xfffffff;
                    }
                    // 在图片四周形成边框
                    else if ((x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH
                            && x < halfW - IMAGE_HALF_WIDTH + FRAME_WIDTH
                            && y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH
                            + IMAGE_HALF_WIDTH + FRAME_WIDTH)
                            || (x > halfW + IMAGE_HALF_WIDTH - FRAME_WIDTH
                            && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH
                            && y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH
                            + IMAGE_HALF_WIDTH + FRAME_WIDTH)
                            || (x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH
                            && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH
                            && y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH
                            - IMAGE_HALF_WIDTH + FRAME_WIDTH)
                            || (x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH
                            && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH
                            && y > halfH + IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH
                            + IMAGE_HALF_WIDTH + FRAME_WIDTH)) {
                        pixels[y * width + x] = 0xfffffff;
                    } else {
                        // 此处可以修改二维码的颜色，可以分别制定二维码和背景的颜色；
                        pixels[y * width + x] = matrix.get(x, y) ? fgColor : bgColor;
                    }
                }
            }

            // 生成二维码图片的格式，使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
            throws WriterException {
        return encode(contents, format, width, height, null);
    }

    private static BitMatrix encode(String contents,
                                    BarcodeFormat format,
                                    int width,
                                    int height,
                                    Map<EncodeHintType, ?> hints) throws WriterException {
        if (contents.isEmpty()) {
            throw new IllegalArgumentException("Found empty contents");
        }

        if (format != BarcodeFormat.QR_CODE) {
            throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
        }

        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
                    height);
        }

        ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
        int quietZone = QUIET_ZONE_SIZE;
        if (hints != null) {
            ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints.get(EncodeHintType.ERROR_CORRECTION);
            if (requestedECLevel != null) {
                errorCorrectionLevel = requestedECLevel;
            }
            Integer quietZoneInt = (Integer) hints.get(EncodeHintType.MARGIN);
            if (quietZoneInt != null) {
                quietZone = quietZoneInt;
            }
        }

        QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
        return renderResult(code, width, height, quietZone);
    }

    private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
        ByteMatrix input = code.getMatrix();
        if (input == null) {
            throw new IllegalStateException();
        }
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        int qrWidth = inputWidth + (quietZone * 2);
        int qrHeight = inputHeight + (quietZone * 2);
        int outputWidth = Math.max(width, qrWidth);
        int outputHeight = Math.max(height, qrHeight);

        int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);

        int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
        int topPadding = (outputHeight - (inputHeight * multiple)) / 2;

        BitMatrix output = new BitMatrix(outputWidth, outputHeight);

        for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
            // Write the contents of this row of the barcode
            for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
                if (input.get(inputX, inputY) == 1) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
            }
        }

        return output;
    }

}
